package com.kongjs.application.service.impl;

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kongjs.application.cache.UserCacheManagerImpl;
import com.kongjs.application.convert.RolesConverter;
import com.kongjs.application.exception.ApiParamCheckException;
import com.kongjs.application.mapper.RolesMapper;
import com.kongjs.application.model.dto.*;
import com.kongjs.application.model.entity.RoleMenu;
import com.kongjs.application.model.entity.RolePermission;
import com.kongjs.application.model.entity.Roles;
import com.kongjs.application.service.*;
import com.kongjs.application.tenant.TenantRoleHierarchyMessage;
import com.kongjs.application.util.PageUtil;
import com.kongjs.common.mybatis.tenant.TenantContextHolder;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Transactional
@Service
public class RolesServiceImpl extends ServiceImpl<RolesMapper, Roles> implements IRolesService {
    @Resource
    private TenantRoleHierarchyMessage tenantRoleHierarchyMessage;
    @Resource
    private RolesConverter rolesConverter;
    @Resource
    private IRoleMenuService roleMenuService;
    @Resource
    private IRoleHierarchyService roleHierarchyService;
    @Resource
    private IRoleMutexService roleMutexService;
    @Resource
    private IRolePermissionService rolePermissionService;

    @Override
    public boolean save(SaveRoleDTO dto) {
        Roles roles = rolesConverter.toEntity(dto);
        save(roles);
        Set<Long> menuIds = dto.getMenuIds();
        if (!CollectionUtils.isEmpty(menuIds)) {
            List<RoleMenu> roleMenus = new ArrayList<>();
            for (Long menuId : menuIds) {
                RoleMenu roleMenu = new RoleMenu();
                roleMenu.setRoleId(roles.getId());
                roleMenu.setMenuId(menuId);
                roleMenus.add(roleMenu);
            }
            roleMenuService.saveBatch(roleMenus);
        }
        Set<Long> permissionIds = dto.getPermissionIds();
        if (!CollectionUtils.isEmpty(permissionIds)) {
            List<RolePermission> rolePermissions = new ArrayList<>();
            for (Long permissionId : permissionIds) {
                RolePermission rolePermission = new RolePermission();
                rolePermission.setRoleId(roles.getId());
                rolePermission.setPermissionId(permissionId);
                rolePermissions.add(rolePermission);
            }
            rolePermissionService.saveBatch(rolePermissions);
        }
        RoleCacheDTO cache = rolesConverter.toCache(roles);
        cache.setPermissionIds(dto.getPermissionIds());
        cache.setMenuIds(dto.getMenuIds());
        UserCacheManagerImpl.getInstance().saveRoleCache(cache);
        tenantRoleHierarchyMessage.publish(TenantContextHolder.getTenantId());
        return true;
    }

    @Override
    public boolean remove(Long id) {
        removeById(id);
        roleMenuService.lambdaUpdate()
                .eq(RoleMenu::getRoleId, id)
                .remove();
        rolePermissionService.lambdaUpdate()
                .eq(RolePermission::getRoleId, id)
                .remove();
        UserCacheManagerImpl.getInstance().removeRoleCache(id);
        tenantRoleHierarchyMessage.publish(TenantContextHolder.getTenantId());
        return true;
    }

    @Override
    public boolean update(UpdateRoleDTO dto) {
        if (!ObjectUtils.isEmpty(dto.getParentId())) {
            if (dto.getId().equals(dto.getParentId())) {
                throw new ApiParamCheckException("设置的上级角色不能与当前角色相同");
            }
            List<Roles> roles = lambdaQuery().eq(Roles::getEnabled, 1).list();
            List<TreeNode<Long>> treeNodes = new ArrayList<>();
            for (Roles role : roles) {
                TreeNode<Long> node = new TreeNode<>();
                node.setId(role.getId());
                node.setParentId(dto.getParentId());
                treeNodes.add(node);
            }
            List<Tree<Long>> trees = TreeUtil.build(treeNodes, null);
            if (!CollectionUtils.isEmpty(trees)) {
                for (Tree<Long> tree : trees) {
                    Tree<Long> parentNode = TreeUtil.getNode(tree, dto.getParentId());
                    if (ObjectUtils.isEmpty(parentNode)) {
                        continue;
                    }
                    List<Long> parentsId = TreeUtil.getParentsId(parentNode, false);
                    // 要设置父级节点存在当前节点作为某个上级,循环引用
                    if (parentsId.contains(dto.getId())) {
                        throw new ApiParamCheckException("设置的上级角色不能是当前角色的下级");
                    }
                }
            }
        }
        Roles roles = rolesConverter.toEntity(dto);
        updateById(roles);
        roleMenuService.lambdaUpdate()
                .eq(RoleMenu::getRoleId, dto.getId())
                .remove();
        Set<Long> menuIds = dto.getMenuIds();
        if (!CollectionUtils.isEmpty(menuIds)) {
            List<RoleMenu> roleMenus = new ArrayList<>();
            for (Long menuId : menuIds) {
                RoleMenu roleMenu = new RoleMenu();
                roleMenu.setRoleId(roles.getId());
                roleMenu.setMenuId(menuId);
                roleMenus.add(roleMenu);
                //roleMenuService.save(roleMenu);
            }
            roleMenuService.saveBatch(roleMenus);
        }
        rolePermissionService.lambdaUpdate()
                .eq(RolePermission::getRoleId, dto.getId())
                .remove();
        Set<Long> permissionIds = dto.getPermissionIds();
        if (!CollectionUtils.isEmpty(permissionIds)) {
            List<RolePermission> rolePermissions = new ArrayList<>();
            for (Long permissionId : permissionIds) {
                RolePermission rolePermission = new RolePermission();
                rolePermission.setRoleId(roles.getId());
                rolePermission.setPermissionId(permissionId);
                rolePermissions.add(rolePermission);
                //rolePermissionService.save(rolePermission);
            }
            rolePermissionService.saveBatch(rolePermissions);
        }
        RoleCacheDTO cache = rolesConverter.toCache(roles);
        cache.setPermissionIds(dto.getPermissionIds());
        cache.setMenuIds(dto.getMenuIds());
        UserCacheManagerImpl.getInstance().saveRoleCache(cache);
        tenantRoleHierarchyMessage.publish(TenantContextHolder.getTenantId());
        return true;
    }

    @Override
    public QueryRoleDTO query(Long id) {
        Roles roles = getById(id);
        return rolesConverter.toDto(roles);
    }

    @Override
    public List<QueryRoleDTO> queryList() {
        List<Roles> roles = list();
        return rolesConverter.toDtos(roles);
    }

    @Override
    public List<QueryRoleDTO> trees() {
        List<Roles> roles = lambdaQuery().eq(Roles::getEnabled, 1).list();
        return rolesConverter.toDtos(roles);
    }

    @Override
    public PageDTO<QueryRoleDTO> page(PageQueryDTO dto, QueryRoleParamDTO paramDTO) {
        Page<Roles> page;
        if (!ObjectUtils.isEmpty(paramDTO)) {
            page = lambdaQuery()
                    .eq(!ObjectUtils.isEmpty(paramDTO.getId()), Roles::getId, paramDTO.getId())
                    .like(StringUtils.hasText(paramDTO.getRoleName()), Roles::getRoleName, paramDTO.getRoleName())
                    .like(StringUtils.hasText(paramDTO.getAuthority()), Roles::getAuthority, paramDTO.getAuthority())
                    .like(StringUtils.hasText(paramDTO.getRemark()), Roles::getRemark, paramDTO.getRemark())
                    .eq(!ObjectUtils.isEmpty(paramDTO.getEnabled()), Roles::getEnabled, paramDTO.getEnabled())
                    .between(!ObjectUtils.isEmpty(paramDTO.getStartDate()) && !ObjectUtils.isEmpty(paramDTO.getEndDate()), Roles::getCreateDate, paramDTO.getStartDate(), paramDTO.getEndDate())
                    .orderByDesc(Roles::getUpdateDate)
                    .page(Page.of(dto.getPageNum(), dto.getPageSize()));
        } else {
            page = lambdaQuery()
                    .orderByDesc(Roles::getUpdateDate)
                    .page(Page.of(dto.getPageNum(), dto.getPageSize()));
        }
        /*IPage<QueryRoleDTO> iPage = page.convert(roles -> {
            QueryRoleDTO queryRoleDTO = rolesConverter.toDto(roles);
            List<RoleMenu> roleMenus = roleMenuService.lambdaQuery().eq(RoleMenu::getRoleId, roles.getId()).list();
            List<Long> menuIds = roleMenus.stream().map(RoleMenu::getMenuId).toList();
            queryRoleDTO.setMenuIds(menuIds);
            List<RoleHierarchy> roleHierarchies = roleHierarchyService.lambdaQuery().eq(RoleHierarchy::getParentRoleId, roles.getId()).list();
            List<Long> childRoleIds = new ArrayList<>(roleHierarchies.stream().map(RoleHierarchy::getChildRoleId).toList());
            childRoleIds.add(roles.getId());
            queryRoleDTO.setRoleIds(childRoleIds);
            List<RolePermission> rolePermissions = rolePermissionService.lambdaQuery().eq(RolePermission::getRoleId, roles.getId()).list();
            List<Long> permissionIds = rolePermissions.stream().map(RolePermission::getPermissionId).toList();
            queryRoleDTO.setRoleIds(permissionIds);
            return queryRoleDTO;
        });*/
        List<Long> roleIds = page.getRecords().stream().map(Roles::getId).collect(Collectors.toList());
        // 获取角色菜单关联
        Map<Long, List<Long>> roleMenuMap = roleMenuService.lambdaQuery()
                .in(RoleMenu::getRoleId, roleIds)
                .list()
                .stream()
                .collect(Collectors.groupingBy(
                        RoleMenu::getRoleId,
                        Collectors.mapping(RoleMenu::getMenuId, Collectors.toList())
                ));
        // 获取角色权限关联
        Map<Long, List<Long>> rolePermissionMap = rolePermissionService.lambdaQuery()
                .in(RolePermission::getRoleId, roleIds)
                .list()
                .stream()
                .collect(Collectors.groupingBy(
                        RolePermission::getRoleId,
                        Collectors.mapping(RolePermission::getPermissionId, Collectors.toList())
                ));
        // 转换结果，直接从预加载的Map中获取关联数据
        IPage<QueryRoleDTO> iPage = page.convert(roles -> {
            QueryRoleDTO queryRoleDTO = rolesConverter.toDto(roles);

            // 获取菜单ID列表
            List<Long> menuIds = roleMenuMap.getOrDefault(roles.getId(), Collections.emptyList());
            queryRoleDTO.setMenuIds(menuIds);

            // 获取权限ID列表
            List<Long> permissionIds = rolePermissionMap.getOrDefault(roles.getId(), Collections.emptyList());
            queryRoleDTO.setPermissionIds(permissionIds);

            return queryRoleDTO;
        });
        return PageUtil.pageDto(iPage);
    }


}
